using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
using Microsoft.Practices.Unity.Utility;

namespace UnityCommonServiceManagerAdapter.Configuration
{
	/// <summary>
	/// This class stores information about which properties to inject,
	/// and will configure the container accordingly.
	/// </summary>
	internal class InjectionPropertyNamed : InjectionMember
	{
		private readonly string propertyName;
		private readonly string keyName;
		private InjectionParameterValue parameterValue;

		/// <summary>
		/// Configure the container to inject the given property name,
		/// resolving the value via the container.
		/// </summary>
		/// <param name="propertyName">Name of the property to inject.</param>
		/// <param name="keyName">The key to use when resolving the property</param>
		public InjectionPropertyNamed(string propertyName, string keyName)
		{
			this.propertyName = propertyName;
			this.keyName = keyName;
		}

		/// <summary>
		/// Add policies to the <paramref name="policies"/> to configure the
		/// container to call this constructor with the appropriate parameter values.
		/// </summary>
		/// <param name="serviceType">Interface being registered, ignored in this implemenation.</param>
		/// <param name="implementationType">Type to register.</param>
		/// <param name="name">Name used to resolve the type object.</param>
		/// <param name="policies">Policy list to add policies to.</param>
		[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods",
				Justification = "Validation is done via Guard class")]
		public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
		{
			Guard.ArgumentNotNull(implementationType, "implementationType");

			var propInfo = implementationType.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

			// Since we use type safe calls this can never happen! ;~)
			//GuardPropertyExists(propInfo, implementationType, propertyName);
			//GuardPropertyIsSettable(propInfo);
			//GuardPropertyIsNotIndexer(propInfo);

			if (parameterValue == null) parameterValue = new ResolvedParameter(propInfo.PropertyType, keyName);

			//GuardPropertyValueIsCompatible(propInfo, parameterValue);

			var selector = GetSelectorPolicy(policies, implementationType, name);
			selector.AddPropertyAndValue(propInfo, parameterValue);
		}

		private static SpecifiedPropertiesSelectorPolicy GetSelectorPolicy(IPolicyList policies, Type typeToInject, string name)
		{
			var key = new NamedTypeBuildKey(typeToInject, name);
			var selector = policies.GetNoDefault<IPropertySelectorPolicy>(key, false);
			if (selector == null || !(selector is SpecifiedPropertiesSelectorPolicy))
			{
				selector = new SpecifiedPropertiesSelectorPolicy();
				policies.Set(selector, key);
			}
			return (SpecifiedPropertiesSelectorPolicy)selector;
		}

		//private static void GuardPropertyExists(PropertyInfo propInfo, Type typeToCreate, string propertyName)
		//{
		//  if (propInfo != null)
		//    return;

		//  var message = string.Format(CultureInfo.CurrentCulture, Strings.NoSuchProperty, typeToCreate.Name, propertyName);
		//  throw new InvalidOperationException(message);
		//}

		//private static void GuardPropertyIsSettable(PropertyInfo propInfo)
		//{
		//  if (propInfo.CanWrite)
		//    return;

		//  var message = ExceptionMessage(Strings.PropertyNotSettable, propInfo.Name, propInfo.DeclaringType);
		//  throw new InvalidOperationException(message);
		//}

		//private static void GuardPropertyIsNotIndexer(PropertyInfo property)
		//{
		//  if (property.GetIndexParameters().Length <= 0)
		//    return;

		//  var message = ExceptionMessage(Strings.CannotInjectIndexer, property.Name, property.DeclaringType);
		//  throw new InvalidOperationException(message);
		//}

		//private static void GuardPropertyValueIsCompatible(PropertyInfo property, InjectionParameterValue value)
		//{
		//  if (value.MatchesType(property.PropertyType))
		//    return;

		//  var message = ExceptionMessage(Strings.PropertyTypeMismatch,
		//                                 property.Name,
		//                                 property.DeclaringType,
		//                                 property.PropertyType,
		//                                 value.ParameterTypeName);
		//  throw new InvalidOperationException(message);
		//}

		//private static string ExceptionMessage(string format, params object[] args)
		//{
		//  for (var i = 0; i < args.Length; ++i)
		//    if (args[i] is Type)
		//      args[i] = ((Type)args[i]).FullName;
		//  return string.Format(CultureInfo.CurrentCulture, format, args);
		//}
	}
}